package br.cns.experiments;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.List;
import java.util.Vector;

import br.cns.TMetric;
import br.cns.experiments.nodePositions.CircularNetwork;
import br.cns.models.BarabasiDensity;
import br.cns.models.ErdosRenyiP;
import br.cns.models.NewmanWatts;
import br.cns.models.TModel;
import br.cns.models.WattsStrogatz;

public class Complenet2013Experiment {
	public static final String HEADER_TABLE = "ID\tp\tDensity\tAC\tEf0\tOnmax";
	public static final String START_FILE_NAME_ERDOS = "complenet/complenet-erdos-";
	public static final String START_FILE_NAME_BARBASI = "complenet/complenet-barabasi-";
	public static final String START_FILE_NAME_WATTS = "complenet/complenet-watts-strogatz-";
	public static final String START_FILE_NAME_NWATTS = "complenet/complenet-newman-watts-";
	public static final String END_FILENAME = ".txt";
	public static final String NEW_LINE = "\n";
	public static final String TAB_SEP = "\t";
	public static final NumberFormat nf = NumberFormat.getInstance();

	public static void main(String[] args) {
		List<TMetric> metricsToCalculate = new Vector<>();
		metricsToCalculate.add(TMetric.DENSITY);
		metricsToCalculate.add(TMetric.ALGEBRAIC_CONNECTIVITY);
		nf.setMinimumFractionDigits(4);
		nf.setMaximumFractionDigits(4);

		try {
			newmanWatts(100, 100, metricsToCalculate);
			newmanWatts(200, 100, metricsToCalculate);
			newmanWatts(400, 100, metricsToCalculate);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static int getK(double density, int n){
		return (int)Math.round(density * (n - 1));
	}

	private static void erdos(int numNodes, int independentRuns, List<TMetric> metricsToCalculate) throws IOException {
		FileWriter fwMetrics = null;
		StringBuilder sbMetrics = new StringBuilder();
		
		FileWriter fwNetwork = null;
		StringBuilder sbNetwork = new StringBuilder();
		
		ComplexNetwork network = null;
		ErdosRenyiP erdos = null;
		Integer[][] matrix = new Integer[numNodes][numNodes];
		

		for (double p = 0.01; p <= 1; p += 0.01) {
			fwMetrics = new FileWriter(new File(START_FILE_NAME_ERDOS + numNodes + "-" + nf.format(p) + END_FILENAME));
			fwNetwork = new FileWriter(new File(START_FILE_NAME_ERDOS + numNodes + "-" + nf.format(p) + "-network" + END_FILENAME));
			
			sbMetrics = new StringBuilder();
			sbNetwork = new StringBuilder();
			
			sbMetrics.append(HEADER_TABLE).append(NEW_LINE);
			for (int id = 1; id <= independentRuns; id++) {
				erdos = new ErdosRenyiP(p);
				network = new ComplexNetwork(id, erdos.transform(matrix), CircularNetwork.getInstance()
						.createNodePositions(numNodes), TModel.ERDOS_RENYI_N_P, metricsToCalculate);
				
				createNetworkContent(sbNetwork, network);
				createMetricsContent(sbMetrics, network, p, id);
			}
			sbMetrics.append(NEW_LINE);
			
			System.out.println("Gravando resultados do experimento " + START_FILE_NAME_ERDOS + numNodes + "-" + nf.format(p) + END_FILENAME);
			
			fwMetrics.write(sbMetrics.toString());
			fwNetwork.write(sbNetwork.toString());
			
			fwMetrics.close();
			fwNetwork.close();
		}
	}
	
	private static void barabasi(int numNodes, int independentRuns, List<TMetric> metricsToCalculate) throws IOException {
		FileWriter fwMetrics = null;
		StringBuilder sbMetrics = new StringBuilder();
		
		FileWriter fwNetwork = null;
		StringBuilder sbNetwork = new StringBuilder();
		
		ComplexNetwork network = null;
		BarabasiDensity barabasi = null;

		for (double p = 0.01; p <= 1; p += 0.01) {
			fwMetrics = new FileWriter(new File(START_FILE_NAME_BARBASI + numNodes + "-" + nf.format(p) + END_FILENAME));
			fwNetwork = new FileWriter(new File(START_FILE_NAME_BARBASI + numNodes + "-" + nf.format(p) + "-network" + END_FILENAME));
			
			sbMetrics = new StringBuilder();
			sbNetwork = new StringBuilder();
			
			sbMetrics.append(HEADER_TABLE).append(NEW_LINE);
			for (int id = 1; id <= independentRuns; id++) {
				barabasi = new BarabasiDensity(p);
				network = new ComplexNetwork(id, barabasi.grow(null, numNodes), CircularNetwork.getInstance()
						.createNodePositions(numNodes), TModel.BARABASI_DENSITY, metricsToCalculate);
				
				createNetworkContent(sbNetwork, network);
				createMetricsContent(sbMetrics, network, p, id);
			}
			sbMetrics.append(NEW_LINE);
			
			System.out.println("Gravando resultados do experimento " + START_FILE_NAME_BARBASI + numNodes + "-" + nf.format(p) + END_FILENAME);
			
			fwMetrics.write(sbMetrics.toString());
			fwNetwork.write(sbNetwork.toString());
			
			fwMetrics.close();
			fwNetwork.close();
		}
	}
	
	private static void watts(int numNodes, int independentRuns, List<TMetric> metricsToCalculate) throws IOException {
		FileWriter fwMetrics = null;
		StringBuilder sbMetrics = new StringBuilder();
		
		FileWriter fwNetwork = null;
		StringBuilder sbNetwork = new StringBuilder();
		
		ComplexNetwork network = null;
		WattsStrogatz watts = null;
		Integer[][] matrix = new Integer[numNodes][numNodes];
		double probability = 0.1;

		for (double p = 0.01; p <= 1; p += 0.01) {
			fwMetrics = new FileWriter(new File(START_FILE_NAME_WATTS + numNodes + "-" + nf.format(p) + END_FILENAME));
			fwNetwork = new FileWriter(new File(START_FILE_NAME_WATTS + numNodes + "-" + nf.format(p) + "-network" + END_FILENAME));
			
			sbMetrics = new StringBuilder();
			sbNetwork = new StringBuilder();
			
			sbMetrics.append(HEADER_TABLE).append(NEW_LINE);
			for (int id = 1; id <= independentRuns; id++) {
				watts = new WattsStrogatz(probability, getK(p, numNodes));
				network = new ComplexNetwork(id, watts.transform(matrix), CircularNetwork.getInstance()
						.createNodePositions(numNodes), TModel.WATTS_STROGATZ, metricsToCalculate);
				
				createNetworkContent(sbNetwork, network);
				createMetricsContent(sbMetrics, network, p, id);
			}
			sbMetrics.append(NEW_LINE);
			
			System.out.println("Gravando resultados do experimento " + START_FILE_NAME_WATTS + numNodes + "-" + nf.format(p) + END_FILENAME);
			
			fwMetrics.write(sbMetrics.toString());
			fwNetwork.write(sbNetwork.toString());
			
			fwMetrics.close();
			fwNetwork.close();
		}
	}
	
	private static void newmanWatts(int numNodes, int independentRuns, List<TMetric> metricsToCalculate) throws IOException {
		FileWriter fwMetrics = null;
		StringBuilder sbMetrics = new StringBuilder();
		
		FileWriter fwNetwork = null;
		StringBuilder sbNetwork = new StringBuilder();
		
		ComplexNetwork network = null;
		NewmanWatts watts = null;
		Integer[][] matrix = new Integer[numNodes][numNodes];
		

		for (double p = 0.01; p <= 1; p += 0.01) {
			fwMetrics = new FileWriter(new File(START_FILE_NAME_NWATTS + numNodes + "-" + nf.format(p) + END_FILENAME));
			fwNetwork = new FileWriter(new File(START_FILE_NAME_NWATTS + numNodes + "-" + nf.format(p) + "-network" + END_FILENAME));
			
			sbMetrics = new StringBuilder();
			sbNetwork = new StringBuilder();
			
			sbMetrics.append(HEADER_TABLE).append(NEW_LINE);
			for (int id = 1; id <= independentRuns; id++) {
				watts = new NewmanWatts(0.1, getK(p, numNodes));
				network = new ComplexNetwork(id, watts.transform(matrix), CircularNetwork.getInstance()
						.createNodePositions(numNodes), TModel.NEWMAN_WATTS, metricsToCalculate);
				
				createNetworkContent(sbNetwork, network);
				createMetricsContent(sbMetrics, network, p, id);
			}
			sbMetrics.append(NEW_LINE);
			
			System.out.println("Gravando resultados do experimento " + START_FILE_NAME_NWATTS + numNodes + "-" + nf.format(p) + END_FILENAME);
			
			fwMetrics.write(sbMetrics.toString());
			fwNetwork.write(sbNetwork.toString());
			
			fwMetrics.close();
			fwNetwork.close();
		}
	}

	private static void createMetricsContent(StringBuilder sbMetrics, ComplexNetwork network, double p, int id) {
		sbMetrics.append(id).append(TAB_SEP);
		sbMetrics.append(nf.format(p)).append(TAB_SEP);
		sbMetrics.append(nf.format(network.getMetricValues().get(TMetric.DENSITY))).append(TAB_SEP);
		sbMetrics.append(nf.format(network.getMetricValues().get(TMetric.ALGEBRAIC_CONNECTIVITY))).append(TAB_SEP);
		
		network.calculateRealEigenvalues();
		
		sbMetrics.append(network.getFirstZeroEvenComponent()).append(TAB_SEP);
		sbMetrics.append(network.getMaxOddComponent()).append(TAB_SEP);
		
		sbMetrics.append(NEW_LINE);
	}

	private static void createNetworkContent(StringBuilder sbNetwork, ComplexNetwork network) {
		for (Integer[] linha : network.getAdjacencyMatrix()){
			for (int valor : linha){
				sbNetwork.append(valor).append(TAB_SEP);	
			}
		}
		sbNetwork.append(NEW_LINE);
	}
}
